const Command = @This();
const std = @import("std");

type: Type,
arg1: []const u8,
arg2: ?usize,

pub const Type = enum {
    arithmetic,
    push,
    pop,
    label,
    goto,
    @"if-goto",
    function,
    @"return",
    call,
};

/// Parses a line of vm code into a Command struct.
pub fn parse(oneline: []const u8) !Command {
    const line = std.mem.trimLeft(u8, oneline, " \t");
    if (line.len == 0) @panic("empty line");

    const first_space = std.mem.indexOfScalar(u8, line, ' ');
    if (first_space == null) { // C_ARITHMETIC or C_RETURN
        return .{
            .type = if (std.mem.eql(u8, line, "return")) .@"return" else .arithmetic,
            .arg1 = line,
            .arg2 = null,
        };
    }

    // skip the cmd name
    var it = std.mem.tokenizeAny(u8, line[first_space.?..], " ");

    const arg1 = it.next().?;
    const arg2 = it.next();
    const cmdtype: Type = switch (line[0]) {
        'p' => if (std.mem.startsWith(u8, line, "push")) .push else .pop,
        'l' => .label,
        'i' => .@"if-goto",
        'g' => .goto,
        'f' => .function,
        'c' => .call,
        else => unreachable,
    };

    return .{
        .type = cmdtype,
        .arg1 = arg1,
        .arg2 = if (arg2 == null) null else try std.fmt.parseInt(usize, arg2.?, 0),
    };
}
